package 每日一题;

import java.util.Arrays;
import java.util.List;

/**
 * @description:
 * @author: 小白白
 * @create: 2021-10-24
 **/

public class No638大礼包 {
    /**
     * 在 LeetCode 商店中， 有 n 件在售的物品。每件物品都有对应的价格。
     * 然而，也有一些大礼包，每个大礼包以优惠的价格捆绑销售一组物品。
     * 给你一个整数数组 price 表示物品价格，其中 price[i] 是第 i 件物品的价格。
     * 另有一个整数数组 needs 表示购物清单，其中 needs[i] 是需要购买第 i 件物品的数量。
     * 还有一个数组 special 表示大礼包，special[i] 的长度为 n + 1 ，
     * 其中 special[i][j] 表示第 i 个大礼包中内含第 j 件物品的数量，且 special[i][n]
     * （也就是数组中的最后一个整数）为第 i 个大礼包的价格。
     * 返回 确切 满足购物清单所需花费的最低价格，你可以充分利用大礼包的优惠活动。
     * 你不能购买超出购物清单指定数量的物品，即使那样会降低整体价格。任意大礼包可无限次购买。
     * <p>
     * 示例 1：
     * 输入：price = [2,5], special = [[3,0,5],[1,2,10]], needs = [3,2]
     * 输出：14
     * 解释：有 A 和 B 两种物品，价格分别为 ¥2 和 ¥5 。
     * 大礼包 1 ，你可以以 ¥5 的价格购买 3A 和 0B 。
     * 大礼包 2 ，你可以以 ¥10 的价格购买 1A 和 2B 。
     * 需要购买 3 个 A 和 2 个 B ， 所以付 ¥10 购买 1A 和 2B（大礼包 2），以及 ¥4 购买 2A 。
     * 示例 2：
     * 输入：price = [2,3,4], special = [[1,1,0,4],[2,2,1,9]], needs = [1,2,1]
     * 输出：11
     * 解释：A ，B ，C 的价格分别为 ¥2 ，¥3 ，¥4 。
     * 可以用 ¥4 购买 1A 和 1B ，也可以用 ¥9 购买 2A ，2B 和 1C 。
     * 需要买 1A ，2B 和 1C ，所以付 ¥4 买 1A 和 1B（大礼包 1），以及 ¥3 购买 1B ， ¥4 购买 1C 。
     * 不可以购买超出待购清单的物品，尽管购买大礼包 2 更加便宜。
     *  
     * 提示：
     * n == price.length
     * n == needs.length
     * 1 <= n <= 6
     * 0 <= price[i] <= 10
     * 0 <= needs[i] <= 10
     * 1 <= special.length <= 100
     * special[i].length == n + 1
     * 0 <= special[i][j] <= 50
     */

    private int result = Integer.MAX_VALUE;

    public int shoppingOffers(List<Integer> price, List<List<Integer>> special, List<Integer> needs) {

        // 全部以单价购买
        int rr = 0;
        for (int i = 0; i < needs.size(); i++) {
            rr += (price.get(i) * needs.get(i));
        }
        this.result = Math.min(this.result, rr);

        // (可选)剔除 [比单价买还贵的,超出数量的] 的礼包
        // 递归回溯礼包购买 当礼包都不符合,但是needs还有剩余,那么就使用单价购买
        this.dfs(price, special, needs, 0);

        return this.result;
    }

    private void dfs(List<Integer> prices, List<List<Integer>> specials, List<Integer> needs, int price) {

        if (price >= this.result) {
            return;
        }

        int count = needs.size();
        for (Integer need : needs) {
            if (need == 0) {
                count--;
            }
        }
        if (count == 0) {
            this.result = Math.min(this.result, price);
            return;
        }

        for (List<Integer> special : specials) {
            boolean flag = true;
            for (int i = 0; i < special.size() - 1; i++) {
                if (needs.get(i) - special.get(i) < 0) {
                    // 超出指定数量,不可
                    flag = false;
                    break;
                }
            }
            if (flag) {
                // 当前礼包可以买,那么就去买
                this.d(special, needs);
                this.dfs(prices, specials, needs, price + special.get(special.size() - 1));
                this.a(special, needs);
            }
        }

        // 礼包走完,直接试试剩余全部用单价购买
        for (int i = 0; i < needs.size(); i++) {
            price += (needs.get(i) * prices.get(i));
        }

        this.result = Math.min(this.result, price);
    }

    private void d(List<Integer> special, List<Integer> needs) {
        for (int i = 0; i < needs.size(); i++) {
            needs.set(i, needs.get(i) - special.get(i));
        }
    }

    private void a(List<Integer> special, List<Integer> needs) {
        for (int i = 0; i < needs.size(); i++) {
            needs.set(i, needs.get(i) + special.get(i));
        }
    }

    public static void main(String[] args) {
        No638大礼包 n = new No638大礼包();
        List<Integer> l1 = Arrays.asList(2, 5);
        List<List<Integer>> l2 = Arrays.asList(Arrays.asList(3, 0, 5), Arrays.asList(1, 2, 10));
        List<Integer> l3 = Arrays.asList(3, 2);
        int result = n.shoppingOffers(l1, l2, l3);
        System.out.println(result);
    }

}
